iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Build on Google AI

在贏家書寫歷史之前:我所看見的 AI,與一位工程師共舞著系列 第 9

Day 09 | 少年 Token 的奇幻漂流(下):深入 Self-Attention

  • 分享至 

  • xImage
  •  

1. Q & K & V!

在 Self-Attention 的運算裡,Q、K、V 並不是什麼神秘的特殊物件,它們純粹就是同一個輸入向量,分別乘上三張不同的權重矩陣後,投影出來的向量。所以它們本質上就是 3 個帶有各自意義的向量

  • Q(Query,查詢):代表「我是當前這個字,我想主動尋找什麼樣的特徵?」(例如:「誰對我施加了動作?」)
  • K(Key,標籤):代表「我具備什麼樣的特徵,公開給大家比對?」(例如:「我是動作」、「我是主體」)
  • V(Value,資訊內容):代表「如果跟我配對成功了,我實際要交出去的實質資訊是什麼?」

https://ithelp.ithome.com.tw/upload/images/20260922/20183607tXxXbTGLUq.png

這裡可能有個疑問:「模型訓練時怎麼知道這些矩陣跟向量的意義的?」
確實,一開始訓練時,模型是不知道這些權重矩陣跟向量的意義的,它們在訓練初期只是一堆亂數,本身沒有意義。那是誰賦予它們意義呢?是「人」。人期望這些向量具有這些意義,並執行對應的運算,所以設計了這套運算流程,期望這樣的架構可以引導模型理解這些權重矩陣與向量的意義。對於模型而言,它只是做完這些運算,然後根據預測結果算 Loss、回傳梯度更新參數。這是我認為最佩服想出這個架構的人的地方!好啦,我只是在自言自語而已哈哈,我們繼續來看它到底怎麼算的~


2. 一張圖了解 Self-Attention 是怎麼運算的?

https://ithelp.ithome.com.tw/upload/images/20260922/20183607A54S4vKjkh.png

假設今天我們輸入的句子是:「我吃蘋果」,而它被 Tokenizer 切成三個 Token,分別是「我」、「吃」、「蘋果」。接著經過 Embedding Table 將這三個轉成 3 個 Embedding 向量,假設維度是 4096 維。

記得我們昨天講的 Multi-Head Attention 嗎?假設在這個例子裡我們總共分了 32 個 Head,那每個 Head 的輸入向量維度就變成了 128 維(4096 / 32),也就是圖中的 x1, x2, x3。上方這張圖就是在一個 Head 內的運算,所以可以想像如果有 32 個 Head,當一個 Embedding 向量輸入進來,就會切成 32 個跟上面一樣的運算同時進行著。

(實際上工程實作時可能不是這樣切,更常見的是先將輸入向量 x 乘上完整的投影矩陣轉出 Q, K, V,再各自切出各個 Head 要的維度區段,例如:[0-127] 給第一個 Head、[128-255] 給第二個 Head...依此類推。)

前幾天一直提到 Attention 這個詞,但好像沒有好好解釋過。Attention 這個東西可以想成是 「關注度」,也就是現在這個 Token 關注其他 Token 的程度有多大。有了對每一個 Token 的關注程度,就能把它對應比例的資訊拿過來跟自己做計算,計算後的結果就會同時包含自己跟對方的資訊在裡面,達到「交換上下文資訊」的目的。這裡我們要做的是 「計算 Token 之間的 Attention」,所以每個 Token 都會跟每個 Token 計算一次關注度,包含自己

假設模型現在正要計算 Token 3「蘋果」 的新向量。剛剛說了給一個 Input 向量都會轉成自己的 Q, K, V 向量,這裡因為我們是計算 Token 3 的 Attention,不會用到 Q1 跟 Q2,因此在圖上沒有畫出來。而我們現在的所在位置就在 「紫色 Q3」 的地方,接著順著箭頭往下走:

步驟一:拿 Q3 去跟所有人的 K 做「向量內積」

(這裡算出來的內積數值只是我隨便假設的)

「蘋果」想知道自己在整個句子裡跟誰最有關係,所以它派出自己的 Q3,分別去跟句子裡每一個人的 K(特徵標籤) 做內積(也就是高中學過的向量內積,兩個向量方向越接近,內積數值越大;反之則越小):

  • 遇到「我」的 K1(代表主體人類):方向不太像,算出來的關聯數值普普通通(如圖中的 S_31,只有 1.1)。
  • 遇到「吃」的 K2(代表進食動作):方向高度相似,關聯數值較高(如圖中的 S_32,拿到 8.2 的高數值)。
  • 遇到「蘋果」自己的 K3(代表水果受詞):自己跟自己也要算一次關聯(圖中的 S_33,拿到 3.5)。

為什麼公式要除以「根號 d_k」?(d_k 是向量的維度,在我們的例子裡是 128)
其實單純是為了防止數值爆炸而已。因為向量維度有 128 維,一百多個數字乘乘加加起來,內積算出來的數值很容易變得非常大。如果數字太大,下一步算出來的結果就會很極端,所以除以維度的開根號,純粹只是把數值縮小、拉回平穩的範圍。

步驟二:Causal Mask:不能偷看後面的字

因為大模型是「猜下一個字」的自回歸生成,所以有一個鐵則:不能偷看後面的字
假設今天的句子後面還有字(例如「我 吃 蘋果 很 開心」),在計算「蘋果」的當下,模型會把來自「很」跟「開心」的內積結果強制設成負無窮大。這樣在後面算權重時,那些字的權重就會直接歸零,等於沒有關注。而在我們圖中「我 吃 蘋果」的例子裡,「蘋果」已經是最後一個字了,所以前面的 1、2、3 都可以正常看到。

步驟三:Softmax:把數值轉成百分比

拿到剛剛那三個數值(1.1、8.2、3.5)之後,模型會把它們送進 Softmax。Softmax 會透過一個數學轉換,把輸入進來的任意數值轉成介於 0 到 1 之間的值,且所有數值的總和為 1。 可以看到圖中 Softmax 那圈虛線外框,代表 Softmax 是把這三個數值綁在一起同時計算,轉成「加總剛好等於 100%(1.0)」的權重比例:

  • 分給「我」的權重 W_1:10%
  • 分給「吃」的權重 W_2:70%(拿到最高權重)
  • 分給「蘋果」自己的權重 W_3:20%

有個重要的細節:「蘋果」也分給了自己 20% 的關注度。這就是為什麼它叫 「Self-Attention(自注意力)」!如果一個字不看自己的 K 跟 V,那它融合完之後就會忘記自己原本是個「水果」,本體特徵就直接消失了。

步驟四:按比例領取大家的 V(實質資訊),加權加總

算好百分比之後,最後一步非常直覺,就是依照剛才算出來的比例,去把大家的 V(實質資訊)打包帶走

  • 拿走「我」的 V1 裡的 10%(吸收一點點主體人類的資訊)
  • 拿走「吃」的 V2 裡的 70%(吸收大量的進食動作資訊)
  • 拿走「蘋果」自己的 V3 裡的 20%(保留原本的水果資訊)

最後,把這三包乘好比例的向量全部加在一起,就得到了圖最下方的最終輸出向量 b_3! 這時候的 b_3,已經不再是一開始那個字典裡孤立的「蘋果」,而是變成了一個 「融合了上下文資訊、代表一顆正在被我吃掉的蘋果」 的全新向量。這樣 Attention 就達到讓模型「理解上下文」的目的了!


3. 可堆疊性與平行運算優勢

特別提醒,我們上面圖示的步驟,只是單獨以 Q3 為主角在某一個 Head 裡的計算。實際上,Q1 與 Q2 也會做一模一樣的操作,所以這層運算最後會輸出 3 個融合語意過後的向量,維度跟最初的 Input 向量完全一樣,接著再被送去做昨天文章介紹的 FFN 與殘差連接。

也正因為輸入的向量數量跟向量維度始終保持一致,這個 Decoder Block 架構得以不斷疊加很多層(例如 32 層、80 層甚至上百層)!

另外,呼應第一天的說法,Self-Attention 比傳統 RNN 快多了! 因為每個 Token 都可以獨立進行自己的 Q、K、V 投影與計算,而且這中間本質上全部都是矩陣乘法,非常適合透過 GPU 的平行計算能力進行硬體加速,這也是 Transformer 在現代大模型時代勝出的關鍵原因。


參考資料


上一篇
Day 08 | 少年 Token 的奇幻漂流(中):單一 Decoder Block 裡面有什麼?
下一篇
Day 10 | 少年 Token 的奇幻漂流(終):淺出 Self-Attention
系列文
在贏家書寫歷史之前:我所看見的 AI,與一位工程師共舞著10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言